﻿
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;

namespace System.Linq.Expressions
{
    public class ObjectDescriptor : ObjectDescriptor<object>
    {
        public ObjectDescriptor(object obj) : base(obj) { }

    }

    public class ObjectDescriptor<T>
    {
        private const BindingFlags c_BindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

        private readonly T f_Object;
        private readonly Type f_ObjectType;
        private DictionaryReadOnly<string, Property> f_Properties;
        private DictionaryReadOnly<string, Field> f_Fields;
        private Func<PropertyInfo, bool> f_PropertiesFilter;
        private Func<FieldInfo, bool> f_FieldsFilter;

        public T Obj { get { return f_Object; } }

        public bool IsNotifyPropertyChanged { get; private set; }

        public DictionaryReadOnly<string, Property> Property
        {
            get
            {
                if(f_Properties != null) return f_Properties;
                var lv_Properties = f_ObjectType.GetProperties(c_BindingFlags)
                            .Where(f_PropertiesFilter ?? (p => true))
                            .Select(p => new Property(f_Object, p)).ToArray();
                f_Properties = new DictionaryReadOnly<string, Property>(lv_Properties.ToDictionary(p => p.Name));
                return f_Properties;
            }
        }

        public Func<PropertyInfo, bool> PropertiesFilter
        {
            get { return f_PropertiesFilter; }
            set
            {
                if(ReferenceEquals(f_PropertiesFilter, value)) return;
                f_PropertiesFilter = value;
                f_Properties = null;
            }
        }
        public Func<FieldInfo, bool> FieldsFilter
        {
            get { return f_FieldsFilter; }
            set
            {
                if(ReferenceEquals(f_FieldsFilter, value)) return;
                f_FieldsFilter = value;
                f_Fields = null;
            }
        }

        public DictionaryReadOnly<string, Field> Fields
        {
            get
            {
                if(f_Fields != null) return f_Fields;
                var lv_Fields = f_ObjectType.GetFields(c_BindingFlags)
                            .Where(f_FieldsFilter ?? (f => true))
                            .Select(p => new Field(f_Object, p)).ToArray();
                f_Fields = new DictionaryReadOnly<string, Field>(lv_Fields.ToDictionary(p => p.Name));
                return f_Fields;
            }
        }


        public ObjectDescriptor(T obj)
        {
            f_Object = obj;
            f_ObjectType = obj.GetType();
            IsNotifyPropertyChanged = obj is INotifyPropertyChanged;
        }
    }
}
